/*
 * Copyright (C) 2017 zhouyou(478319399@qq.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.shuyihui.rxhttp.request

import com.google.gson.reflect.TypeToken
import com.shuyihui.rxhttp.cache.model.CacheResult
import com.shuyihui.rxhttp.callback.CallBack
import com.shuyihui.rxhttp.callback.CallBackProxy
import com.shuyihui.rxhttp.callback.CallClazzProxy
import com.shuyihui.rxhttp.func.ApiResultFunc
import com.shuyihui.rxhttp.func.CacheResultFunc
import com.shuyihui.rxhttp.func.RetryExceptionFunc
import com.shuyihui.rxhttp.model.ApiResult
import com.shuyihui.rxhttp.subsciber.CallBackSubscriber
import com.shuyihui.rxhttp.utils.RxUtil._io_main
import com.shuyihui.rxhttp.utils.RxUtil._main
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import okhttp3.ResponseBody
import java.lang.reflect.Type

/**
 *
 * 描述：get请求
 */
class GetRequest(url: String?) : BaseRequest<GetRequest>(url) {
    fun <T> execute(clazz: Class<T>?): Observable<T> {
        return execute(object : CallClazzProxy<ApiResult<T>?, T>(clazz) {})
    }

    fun <T> execute(type: Type?): Observable<T> {
        return execute(object : CallClazzProxy<ApiResult<T>?, T>(type) {})
    }

    fun <T> execute(proxy: CallClazzProxy<out ApiResult<T>?, T>): Observable<T> {
        return build()!!.generateRequest()
            .map(ApiResultFunc<T>(proxy.type!!))
            .compose(if (isSyncRequest) _main() else _io_main())
            .compose(rxCache!!.transformer<T>(cacheMode, proxy.callType))
            .retryWhen(
                RetryExceptionFunc(
                    retryCount,
                    retryDelay.toLong(),
                    retryIncreaseDelay.toLong()
                )
            )
            .compose { it.map(CacheResultFunc<T>()) }
    }

    fun <T> execute(callBack: CallBack<T>?): Disposable {
        return execute(object : CallBackProxy<ApiResult<T>?, T>(callBack) {})
    }

    fun <T> execute(proxy: CallBackProxy<out ApiResult<T>?, T>): Disposable {
        val observable = build()!!.toObservable(
            apiManager!![url!!, params.urlParamsMap], proxy
        )
        return if (CacheResult::class.java != proxy.callBack!!.rawType) {
            observable.compose { upstream ->
                upstream.map(
                    CacheResultFunc()
                )
            }.subscribeWith(CallBackSubscriber(context, proxy.callBack))
        } else {
            observable.subscribeWith(CallBackSubscriber(context, proxy.callBack as CallBack<CacheResult<T>>))
        }
    }

    private fun <T> toObservable(
        observable: Observable<out ResponseBody?>,
        proxy: CallBackProxy<out ApiResult<T>?, T>?
    ): Observable<CacheResult<T>> {
        return observable.map(ApiResultFunc<T>(proxy?.type ?: object :
            TypeToken<ResponseBody?>() {}.type))
            .compose(if (isSyncRequest) _main() else _io_main<T>())
            .compose(rxCache!!.transformer(cacheMode, proxy!!.callBack!!.type))
            .retryWhen(
                RetryExceptionFunc(
                    retryCount,
                    retryDelay.toLong(),
                    retryIncreaseDelay.toLong()
                )
            ) as Observable<CacheResult<T>>
    }

    override fun generateRequest(): Observable<ResponseBody?> {
        return apiManager!![url!!, params.urlParamsMap]
    }
}